package com.finalproject.endice9x9;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;

import android.app.FragmentManager;
import android.content.Context;

//Game Controller provides interaction between View and Model
public class GameController {
	private GameBoardFragment view;
	private GameControlFragment control;
	private GameModel model;
	private Context context;
	private ArrayList<String> levels = new ArrayList<String>();
	
	private String strOldModel = "";
	private int oldLevelNumber = 0;
	private int levelNumber = 0;
	private int levelMaxNumber = 0;
	
	enum CONTROLLER_MODE{
		PLAY,
		EDIT,
		NEW
	};
	
	private CONTROLLER_MODE controllerMode = CONTROLLER_MODE.PLAY;
		
	public GameController(Context context) {
		this.context = context;
	}
	
	//Generate empty level state 
	private String emptyLevel() {
		String str = "";
		for(int i = 0; i < GameModel.ROWS * GameModel.COLS * 2; i++ ) {
			str += ".";
		}
		
		return str;
	}
	
	//Load levels from levels file located in sand box
	private void loadLevels() {
		try {
			InputStream in = context.openFileInput("levels");
			BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 
			for(String line = reader.readLine(); line != null; line = reader.readLine()) {
				levels.add(line);
			}
			reader.close();
			in.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}	
		
		if(levels.size() == 0) {
			levels.add(emptyLevel());
		}
		
		levelInit();
	}
	
	//Save levels
	private void saveLevels() {
		try {
			OutputStream out = context.openFileOutput("levels",  Context.MODE_PRIVATE);
			Writer writer = new OutputStreamWriter(out); 
			for(String level : levels) {
				writer.write(level + "\n");
			}
			writer.close();
			out.close();			
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
	
	//Loads highest completed level, current level, and current level model state from current state file 
	private void loadCurrentState() {
		try {
			InputStream in = context.openFileInput("current_state");
			BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 
			String strMaxLevel = reader.readLine();
			String strLevel = reader.readLine();
			String strModel = reader.readLine();

			if(strMaxLevel != null && strLevel != null && strModel != null) {
				levelMaxNumber = Integer.parseInt(strMaxLevel);
				levelNumber = Integer.parseInt(strLevel);
				if(levelNumber < levels.size()) {
					model.initialize(strModel);
				} else {
					levelNumber = 0;
				}				
			}
			
			reader.close();
			in.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}			
	}
	
	//Stores views and model interface in member variables for future use
	public void setViewModel(GameBoardFragment view, GameControlFragment control, GameModel model) {		
		this.view = view;
		this.control = control;
		this.model = model;	
		
		loadLevels();
		loadCurrentState();
	}
	
	//Initialize model with current level
	private void levelInit() {
		String level = levels.get(levelNumber);
		model.initialize(level);	
	}
	
	//Update game board view using info from model
	//Scan model elements and update game board view using corresponding tiles 
	public void updateView(){
		for(int row = 0; row < GameModel.ROWS; row++ ) {
			for(int col = 0; col < GameModel.COLS; col++) {
				 byte dest = model.getDest(row, col);
				 byte state = model.getState(row, col);
				 if ( state > 0) {
					 switch(state) {
					 case 1:
						 view.setTile(row, col, GameBoardFragment.TILE_TYPES.DICE1);
						 break;
					 case 2:
						 view.setTile(row, col, GameBoardFragment.TILE_TYPES.DICE2);
						 break;
					 case 3:
						 view.setTile(row, col, GameBoardFragment.TILE_TYPES.DICE3);
						 break;
					 case 4:
						 view.setTile(row, col, GameBoardFragment.TILE_TYPES.DICE4);
						 break;
					 case 5:
						 view.setTile(row, col, GameBoardFragment.TILE_TYPES.DICE5);
						 break;
					 case 6:
						 view.setTile(row, col, GameBoardFragment.TILE_TYPES.DICE6);
						 break;
					 case GameModel.STATEMOVEZERO:
						 view.setTile(row, col, GameBoardFragment.TILE_TYPES.DICE0);
						 break;					 
					 }
				 } else if(dest > 0){
					 view.setTile(row, col, GameBoardFragment.TILE_TYPES.DESTINATION);	 
				 } else {
					 view.setTile(row, col, GameBoardFragment.TILE_TYPES.EMPTY);
				 }
			}
		}
		
		view.setLevelNumber(levelNumber+1);		
	}
	
	//Update control fragment, enabling or disabling corresponding arrows
	public void updateControl() {
		if(controllerMode == CONTROLLER_MODE.EDIT) {
			control.setPrevEnabled(true);
			control.setNextEnabled(true);
		} else {
			if(levelNumber > 0) {
				control.setPrevEnabled(true);
			} else {
				control.setPrevEnabled(false);
			}
			
			if(levelMaxNumber > levelNumber) {
				control.setNextEnabled(true);
			} else {
				control.setNextEnabled(false);
			}
		}
	}
	
	//Translate view die move event into corresponding model commands
	//Checks if level completed and displays corresponding message
	public void diceMove(int downRow, int downCol, int upRow, int upCol) {
		if(downRow - upRow == 0) {
			if(downCol - upCol > 0) {
				model.moveLeft(downRow, downCol);
			} else if(downCol - upCol < 0) {
				model.moveRight(downRow, downCol);
			}
		} else if(downCol - upCol == 0) {
			if(downRow - upRow > 0) {
				model.moveUp(downRow, downCol);
			} else if (downRow - upRow < 0) {
				model.moveDown(downRow, downCol);
			}
		}
		
		updateView();
		
		if(model.isLevelComplete()) {
			if(levelNumber == levels.size() - 1){ 
				FragmentManager fm = view.getActivity().getFragmentManager();
				InfoFragment dialog = new InfoFragment(R.string.congrat_info, 
						R.string.congrat_text);			
				dialog.show(fm, InfoFragment.DIALOG_INFO);	
			}else {
				FragmentManager fm = view.getActivity().getFragmentManager();
				InfoFragment dialog = new InfoFragment(R.string.title_info, 
						R.string.level_complete);			
				dialog.show(fm, InfoFragment.DIALOG_INFO);	
			}
			
			levelMaxNumber = levelMaxNumber <= levelNumber ? levelNumber + 1 : levelMaxNumber;
			updateControl();			
		}
	}
	
	//In level edit mode cycle throw possible tile states 
	public void diceClick(int row, int col) {
		if(controllerMode != CONTROLLER_MODE.PLAY) {
			model.cycleTile(row, col);
			updateView();
		}
	}
	
	//Update level up
	public void levelUp(){
		if(levelNumber < levels.size()-1) {
			levelNumber++;
			levelInit();
			updateView();
			updateControl();
		}
	}
	
	//Update level down
	public void levelDown() {
		if(levelNumber > 0) {
			levelNumber--;
			levelInit();
			updateView();
			updateControl();
		}
	}
	
	//Reset level to its original state
	public void levelReset() {
		levelInit();
		updateView();
	}

	//Add new empty level to game model after current level
	public void newLevel() {
		controllerMode = CONTROLLER_MODE.NEW;
		strOldModel = model.getModel();
		levelNumber++;
		levels.add(levelNumber, emptyLevel());
		levelInit();	
		updateView();
	}

	//Delete current level from model
	public void deleteLevel() {
		controllerMode = CONTROLLER_MODE.PLAY;
		if(levels.size() > 1) {
			levels.remove(levelNumber);
			if(levelNumber >= levels.size())
				levelNumber = levels.size()-1;
		} else {
			levels.set(0, emptyLevel());
		}
		
		saveLevels();
		levelInit();
		updateView();
	}

	//Enable level edit and save original model state
	public void editLevel() {
		controllerMode = CONTROLLER_MODE.EDIT;
		strOldModel = model.getModel();
		oldLevelNumber = levelNumber;
		updateControl();
	}

	//Save level and restore play mode
	public void saveLevel() {
		controllerMode = CONTROLLER_MODE.PLAY;
		levels.set(levelNumber, model.getModel());
		saveLevels();
		updateControl();
	}

	//Cancel level editing and restore original state and play mode 
	public void cancelLevel() {	
		if(controllerMode == CONTROLLER_MODE.EDIT) {
			controllerMode = CONTROLLER_MODE.PLAY;
			model.initialize(strOldModel);
			levelNumber = oldLevelNumber;
			updateView();
		} else if(controllerMode == CONTROLLER_MODE.NEW) {
			deleteLevel();
		}
		updateControl();
	}
	
	//Save current game state including highest completed level, current level, and current level state
	public void saveCurrentState() {
		try {
			OutputStream out = context.openFileOutput("current_state",  Context.MODE_PRIVATE);
			Writer writer = new OutputStreamWriter(out); 
			if(controllerMode == CONTROLLER_MODE.PLAY) {
				writer.write(Integer.toString(levelMaxNumber) + "\n");
				writer.write(Integer.toString(levelNumber) + "\n");
				writer.write(model.getModel());
			} 
			writer.close();
			out.close();			
		} catch (Exception e) {
			e.printStackTrace();
		}	
	}
}
